#include <Arduino.h>
#include <driver/timer.h> // 使用定时器所需头文件
#include <WiFi.h> // 使用wifi功能所需头文件
// https://shop64022682.taobao.com/ DIY模块地址
// 丝杆 导程3mm 步进电机步进角18° 20步/圈  0.15毫米/步

const String devName = "ESP32-STEPPING"; // 自定义设备名称
const int stepPin = 27; // 步进引脚为PIO27引脚，接A4988的STEP引脚
const int dirPin = 26; // 步进方向引脚为PIO26引脚，接A4988的DIR引脚
static unsigned int stepCnt = 0; // 步进计数器，记录清零后当前前进步数
static unsigned int dir = 0; // 步进方向

const String password = "12345678"; // AP模式下的密码
WiFiServer server(80); // 设置服务器端口为80（HTTP默认端口）

// 初始化wifi函数声明
void WIFI_Init();
// 定时器初始化函数声明，设置定时器参数
void Time_Init(unsigned int usCnt);
// 设置步进引脚频率（单位Hz）
void SetStepFreq(unsigned int freq);

void setup() {  
  pinMode(stepPin, OUTPUT); // 将步进引脚设为输出模式
  pinMode(dirPin, OUTPUT); // 将步进方向引脚设为输出模式
  Serial.begin(9600); // 设置串口通信波特率为9600，启动串口
  Time_Init(1000000); // 设置定时器频率为1Hz，但不启动定时器
  Serial.print(" Device ready: "); // 向串口输出字符串（不带换行）
  Serial.print(devName); // 向串口输出字符串变量（不带换行）
  Serial.println(" !!! "); // 向串口输出字符串（带换行）
  Serial.println(); // 向串口输出换行
  WIFI_Init(); // 初始化WIFI
  server.begin(); // 启动HTTP服务
}
// 主循环，循环读取80端口接收到的数据
void loop() {
  delay(500);  
  WiFiClient client = server.available();   // 监听接入的客户设备
  // Serial.print("设备连接数：");
  // Serial.println(WiFi.softAPgetStationNum()); // 获取接入客户设备数量
  if (client) {                             // 如果有设备接入
    if (client.connected()) {               // 判断客户端是否连接成功
      if (client.available()) {             // 判断客户端是否有数据发送
        String request = client.readStringUntil('\r');
        // Serial.println("客户端请求信息：" + request);
        if (request.indexOf("/100Hz") != -1) // 如果客户端请求中含有100Hz
        {
          SetStepFreq(100); // 将步进引脚频率设为100Hz
        }
        if (request.indexOf("/300Hz") != -1) // 如果客户端请求中含有300Hz
        {
          SetStepFreq(300); // 将步进引脚频率设为300Hz
        }
        if (request.indexOf("/500Hz") != -1) // 如果客户端请求中含有500Hz
        {
          SetStepFreq(500); // 将步进引脚频率设为500Hz
        }
        if (request.indexOf("/STOP") != -1) 
        {
          timer_pause(TIMER_GROUP_0,TIMER_0);
        }
        // 向客户端发送网页源码
        client.println("HTTP/1.1 200 0K");
        client.println("Content-Type: text/html");
        client.println("");
        client.println("<!DOCTYPE HTML>");
        client.println("<html>");
        client.println("<head></head><body>") ;
        client.println("<h1>"+ devName +" Web server</h1><p>.</p><p>.</p>");
        client.println("<p>Click <a href=\"/100Hz\">100Hz</a> (PWM: 100Hz)</p><p>.</p><p>.</p>");
        client.println("<p>Click <a href=\"/300Hz\">300Hz</a> (PWM: 300Hz)</p><p>.</p><p>.</p>");
        client.println("<p>Click <a href=\"/500Hz\">500Hz</a> (PWM: 500Hz)</p><p>.</p><p>.</p>");
        client.println("<p>Click <a href=\"/STOP\">STOP</a> (Stepping motor stops moving.) </p>");
        client.println("</body>") ;
      }
    }
    client.stop();
  }

}
// 初始化wifi
void WIFI_Init() {
  bool result = WiFi.softAP(devName.c_str(),password.c_str()); //启动AP模式
  if(result) {    
    // 串口输出WIFI信息，方便用户连接
    Serial.println("AP mode started");
    Serial.println("WIFI 名称:" + devName);
    Serial.println("WIFI 密码:" + password);
    Serial.print("WIFI连接成功后,请在浏览器中输入本机网址:http://");
    Serial.println( WiFi.softAPIP());
  }else {
    Serial.println("AP mode failed");
  }
}

//定时器中断回调函数,定时器中断响时，执行此函数中代码
void IRAM_ATTR timer_group0_isr(void *para){
   int timer_idx = (int) para;
    uint32_t intr_status = TIMERG0.int_st_timers.val; //获取定时器中断状态 
    if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
        TIMERG0.int_clr_timers.t0 = 1;  //清除中断标志
    } 
    TIMERG0.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN;  //再次使能alarm
    stepCnt++; // 计数器累加
    // 计数器为奇数时步进引脚输出高电平，计数器为偶数时步进引脚输出低电平
    // 计数器每加2为一个电平周期，对应电机前进一步    
    digitalWrite(stepPin, stepCnt % 2); 
    // 丝杆 导程3mm 步进电机步进角18° 20步/圈  0.15毫米/步 400步（即800/2）行进60mm
    if (stepCnt > 800)
    {
        // 切换电机运动方向
        if (dir == 0)
        {
          dir = 1;
        }
        else
        {
          dir = 0;
        }
        digitalWrite(dirPin, dir);
        stepCnt = 0; // 计数器清零
    }
}
// 定时器初始化函数，设置定时器参数
void Time_Init(unsigned int usCnt) {  
	timer_config_t config;// 设置定时器初始化参数
  config.divider = 8; //分频系数
	config.counter_dir = TIMER_COUNT_UP; //计数方式为向上计数
	config.counter_en = TIMER_PAUSE; //调用timer_init函数以后不启动计数,调用timer_start时才开始计数
	config.alarm_en = TIMER_ALARM_EN; //到达计数值启动报警（计数值溢出，进入中断）
  config.auto_reload = 1; //自动重新装载预装值
  // 初始化定时器
	timer_init(TIMER_GROUP_0,TIMER_0,&config); 
	// 设置定时器预装值
	timer_set_counter_value(TIMER_GROUP_0,TIMER_0,0x00000000ULL); 
	// 设置报警阈值 usCnt[定时usCnt 微秒] (TIMER_BASE_CLK[定时器时钟/8[分频系数]/1000/1000[延时为us级别，因此/1000/1000])
	timer_set_alarm_value(TIMER_GROUP_0,TIMER_0,usCnt*(TIMER_BASE_CLK/8/1000/1000)); //TIMER_BASE_CLK 为80M
	// 定时器中断使能
	timer_enable_intr(TIMER_GROUP_0,TIMER_0);
	// 注册定时器中断函数
	timer_isr_register(TIMER_GROUP_0,TIMER_0,
			timer_group0_isr,  //定时器中断回调函数
			(void*)TIMER_0,    //传递给定时器回调函数的参数
			ESP_INTR_FLAG_IRAM, //把中断放到 IRAM 中
			NULL //调用成功以后返回中断函数的地址,一般用不到
			);
	// 启动定时器
	// timer_start(TIMER_GROUP_0,TIMER_0); // 屏蔽此句定时器则不启动，初始状态步进电机不运动
}

// 设置步进引脚频率（单位Hz）
void SetStepFreq(unsigned int freq) {
    timer_pause(TIMER_GROUP_0,TIMER_0); // 停止定时器运行
    timer_set_alarm_value(TIMER_GROUP_0,TIMER_0,1000*1000/2/freq*(TIMER_BASE_CLK/8/1000/1000));
    timer_start(TIMER_GROUP_0,TIMER_0); // 启动定时器
}